{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Single Source Shortest Path (SSSP)\n",
    "\n",
    "In this notebook, we will use cuGraph to compute the shortest path from a starting vertex to everyother vertex in our training dataset.\n",
    "\n",
    "Notebook Credits\n",
    "* Original Authors: Bradley Rees and James Wyles\n",
    "* available since rerlease 0.6\n",
    "* Last Edit: 02/04/2020\n",
    "\n",
    "\n",
    "RAPIDS Versions: 0.12.0   \n",
    "\n",
    "Test Hardware\n",
    "\n",
    "* GV100 32G, CUDA 10.0\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "## Introduction\n",
    "\n",
    "Single source shortest path computes the shortest paths from the given starting vertex to all other reachable vertices. \n",
    "\n",
    "To compute SSSP for a graph in cuGraph we use:\n",
    "**cugraph.sssp(G, source)**\n",
    "\n",
    "Input\n",
    "* __G__: cugraph.Graph object\n",
    "* __source__: int, Index of the source vertex\n",
    "\n",
    "Returns \n",
    "* __df__:  a cudf.DataFrame object with two columns:\n",
    "    * df['vertex']: The vertex identifier for the vertex\n",
    "    * df['distance']: The computed distance from the source vertex to this vertex\n",
    "    * df['predecessor']: The predecessor vertex along this paths.  Allows paths to be recreated\n",
    "\n",
    "\n",
    "## cuGraph Notice \n",
    "The current version of cuGraph has some limitations:\n",
    "\n",
    "* Vertex IDs need to be 32-bit integers.\n",
    "* Vertex IDs are expected to be contiguous integers starting from 0.\n",
    "\n",
    "cuGraph provides the renumber function to mitigate this problem. Input vertex IDs for the renumber function can be either 32-bit or 64-bit integers, can be non-contiguous, and can start from an arbitrary number. The renumber function maps the provided input vertex IDs to 32-bit contiguous integers starting from 0. cuGraph still requires the renumbered vertex IDs to be representable in 32-bit integers. These limitations are being addressed and will be fixed soon."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test Data\n",
    "We will be using the Zachary Karate club dataset \n",
    "*W. W. Zachary, An information flow model for conflict and fission in small groups, Journal of\n",
    "Anthropological Research 33, 452-473 (1977).*\n",
    "\n",
    "\n",
    "![Karate Club](../img/zachary_black_lines.png)\n",
    "\n",
    "This is a small graph which allows for easy visual inspection to validate results.  \n",
    "__Note__: The Karate dataset starts with vertex ID 1 which the cuGraph analytics assume a zero-based starting ID.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import needed libraries\n",
    "import cudf\n",
    "import cugraph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Read the data and adjust the vertex IDs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Test file  - using the clasic Karate club dataset.  \n",
    "datafile='../data/karate-data.csv'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "gdf = cudf.read_csv(datafile, names=[\"src\", \"dst\"], delimiter='\\t', dtype=[\"int32\", \"int32\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Need to shift the vertex IDs to start with zero rather than one (next version of cuGraph will fix this issue)\n",
    "gdf[\"src_0\"] = gdf[\"src\"] - 1\n",
    "gdf[\"dst_0\"] = gdf[\"dst\"] - 1\n",
    "\n",
    "# The SSSP algorithm requires that there are weights.  Just use 1.0 here (equivalent to BFS)\n",
    "gdf[\"data\"] = 1.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>src</th>\n",
       "      <th>dst</th>\n",
       "      <th>src_0</th>\n",
       "      <th>dst_0</th>\n",
       "      <th>data</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>4</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>5</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   src  dst  src_0  dst_0  data\n",
       "0    1    2      0      1   1.0\n",
       "1    1    3      0      2   1.0\n",
       "2    1    4      0      3   1.0\n",
       "3    1    5      0      4   1.0\n",
       "4    1    6      0      5   1.0"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gdf.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a Graph and call SSSP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a Graph \n",
    "G = cugraph.Graph()\n",
    "G.from_cudf_edgelist(gdf, source='src_0', destination='dst_0', edge_attr='data')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call cugraph.sssp to get the distances from vertex 1:\n",
    "df = cugraph.sssp(G, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(0) path: [1, 0]\n",
      "(1) path: [1]\n",
      "(2) path: [1, 2]\n",
      "(3) path: [1, 3]\n",
      "(4) path: [1, 0, 4]\n",
      "(5) path: [1, 0, 5]\n",
      "(6) path: [1, 0, 6]\n",
      "(7) path: [1, 7]\n",
      "(8) path: [1, 30, 8]\n",
      "(9) path: [1, 2, 9]\n",
      "(10) path: [1, 0, 10]\n",
      "(11) path: [1, 0, 11]\n",
      "(12) path: [1, 0, 12]\n",
      "(13) path: [1, 13]\n",
      "(14) path: [1, 13, 33, 14]\n",
      "(15) path: [1, 13, 33, 15]\n",
      "(16) path: [1, 0, 5, 16]\n",
      "(17) path: [1, 17]\n",
      "(18) path: [1, 13, 33, 18]\n",
      "(19) path: [1, 19]\n",
      "(20) path: [1, 13, 33, 20]\n",
      "(21) path: [1, 21]\n",
      "(22) path: [1, 13, 33, 22]\n",
      "(23) path: [1, 13, 33, 23]\n",
      "(24) path: [1, 0, 31, 24]\n",
      "(25) path: [1, 0, 31, 25]\n",
      "(26) path: [1, 13, 33, 26]\n",
      "(27) path: [1, 2, 27]\n",
      "(28) path: [1, 2, 28]\n",
      "(29) path: [1, 13, 33, 29]\n",
      "(30) path: [1, 30]\n",
      "(31) path: [1, 0, 31]\n",
      "(32) path: [1, 30, 32]\n",
      "(33) path: [1, 13, 33]\n"
     ]
    }
   ],
   "source": [
    "# Print the paths\n",
    "# Not using the filterred dataframe to ensure that vertex IDs match row IDs\n",
    "for i in range(len(df)) :\n",
    "    v = df['vertex'][i]   \n",
    "    d = int(df['distance'][v])\n",
    "   \n",
    "    path = [None] *  ( int(d) + 1)\n",
    "    path[d] = v\n",
    "    \n",
    "    while d > 0 :\n",
    "        v = df['predecessor'][v]\n",
    "        d = int(df['distance'][v])\n",
    "        path[d] = v\n",
    "        \n",
    "    print( \"(\" + str(i) + \") path: \" + str(path))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are a number of vertices with the same distance of 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "___\n",
    "Copyright (c) 2019, NVIDIA CORPORATION.\n",
    "\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\");  you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n",
    "\n",
    "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n",
    "___"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "cugraph_dev",
   "language": "python",
   "name": "cugraph_dev"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
